﻿using Furion;
using Furion.DependencyInjection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using Queer.Cache;
using Queer.Entity;
using Queer.Enum;
using Queer.IBusiness.SystemManage;
using Queer.Util;
using Queer.Util.Helper;
using SqlSugar;
using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Queer.WebApi.Areas.WeixinOpen
{

    /// <summary>
    /// 后台系统 验证token和记录日志
    /// </summary>
    public class AuthorizeFilterAttribute : ActionFilterAttribute, ITransient
    {
        private readonly OperatorCache _operator;
        private readonly ApiAuthorizeCache _apiAuthorizeCache;
        private readonly IMenuAuthorizeBLL _menuAuthorizeBLL;

        /// <summary>
        /// 忽略校验的URL
        /// </summary>
        private readonly static string[] ignoreUrl = { "/OnLogin" };

        public AuthorizeFilterAttribute(OperatorCache operatord, IMenuAuthorizeBLL menuAuthorizeBLL, ApiAuthorizeCache apiAuthorizeCache)
        {
            _operator = operatord;
            _menuAuthorizeBLL = menuAuthorizeBLL;
            _apiAuthorizeCache = apiAuthorizeCache;
        }

        /// <summary>
        /// Api记录操作配置
        /// </summary>
        public static string LogAllApi = GlobalContext.SystemConfig.LogAllApi;

        /// <summary>
        /// 过滤执行
        /// </summary>
        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var httpPath = context.HttpContext.Request.Path.ToString();

            // 类似计时器
            Stopwatch sw = new Stopwatch();
            sw.Start();

            // 获取用户信息
            ActionExecutedContext resultContext = null;

            // 如果有忽略的URL，就跳出
            if (ignoreUrl.Any(a => httpPath.Contains(a)))
            {
                resultContext = await next();
            }
            else
            {

                // 执行
                resultContext = await next();

            }

            #region 保存日志

            // 如果配置了仅记录报错日志
            if (LogAllApi.ToUpper() == "ERROR" && resultContext.Exception == null)
                return;

            LogApiEntity logApiEntity = new LogApiEntity();
            logApiEntity.ExecuteUrl = httpPath;
            logApiEntity.LogStatus = OperateStatusEnum.Success.ParseToInt();

            #region 尝试获取请求参数

            switch (context.HttpContext.Request.Method.ToUpper())
            {
                case "GET":
                    logApiEntity.ExecuteParam = context.HttpContext.Request.QueryString.Value.ParseToString();
                    break;

                case "POST":
                    if (context.ActionArguments?.Count > 0)
                    {
                        if (context.HttpContext.Request.QueryString.HasValue)
                            logApiEntity.ExecuteUrl += context.HttpContext.Request.QueryString.Value.ParseToString();

                        if (context.ActionArguments?.Count > 0)
                            logApiEntity.ExecuteParam = TextHelper.GetSubString(JsonConvert.SerializeObject(context.ActionArguments), 4000);
                    }
                    else
                        logApiEntity.ExecuteParam = context.HttpContext.Request.QueryString.Value.ParseToString();

                    break;
            }

            #endregion

            //if (user != null)
            //    logApiEntity.CreateUserId = user.UserId;

            // 异常信息
            SetExceptionMsg(resultContext, context, logApiEntity);

            // 计时器结束
            sw.Stop();

            logApiEntity.ExecuteTime = sw.ElapsedMilliseconds.ParseToInt();
            logApiEntity.IpAddress = NetHelper.Ip;

            // 记录日志
            await SaveLogAPI(logApiEntity);

            #endregion
        }

        /// <summary>
        /// 设置异常信息到数据库实体
        /// </summary>
        private void SetExceptionMsg(ActionExecutedContext resultContext, ActionExecutingContext context, LogApiEntity logApiEntity)
        {
            if (resultContext.Exception != null)
            {
                #region 异常获取

                StringBuilder sbException = new StringBuilder();
                Exception exception = resultContext.Exception;
                sbException.AppendLine(exception.Message);
                while (exception.InnerException != null)
                {
                    sbException.AppendLine(exception.InnerException.Message);
                    exception = exception.InnerException;
                }
                sbException.AppendLine(TextHelper.GetSubString(resultContext.Exception.StackTrace, 8000));

                #endregion

                logApiEntity.ExecuteResult = sbException.ToString();
                logApiEntity.LogStatus = OperateStatusEnum.Fail.ParseToInt();
            }
            else
            {
                ObjectResult result = context.Result as ObjectResult;
                if (result != null)
                {
                    logApiEntity.ExecuteResult = JsonConvert.SerializeObject(result.Value);
                    logApiEntity.LogStatus = OperateStatusEnum.Success.ParseToInt();
                }
            }
        }

        /// <summary>
        /// 保存日志到数据库
        /// </summary>
        private async Task SaveLogAPI(LogApiEntity logApiEntity)
        {
            try
            {
                logApiEntity.ExecuteParam = TextHelper.GetSubString(logApiEntity.ExecuteParam, 4000);
                logApiEntity.ExecuteResult = TextHelper.GetSubString(logApiEntity.ExecuteResult, 4000);
                logApiEntity.Id = IdGeneratorHelper.Instance.GetId();
                logApiEntity.CreateTime = DateTime.Now;

                // 踩坑：此处使用三方包进行保存，因为当接口采用自动事务管理时，如果此处也使用EF，会被一并回滚，导致无法正常记录
                var logApiDB = App.GetService<ISqlSugarRepository<LogApiEntity>>();
                await logApiDB.InsertAsync(logApiEntity);
            }
            catch (Exception ex)
            {
                LogHelper.Error("日志记录到数据库时发生错误", ex);
            }
        }
    }
}
